/*
 * Copyright (C) 2008,2009,2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#ifndef _ecode_h
#define _ecode_h

#include "etypes.h"

#define eglobal_variable(tag, symbol)					\
    evariable(true, tag, symbol)

#define elocal_variable(tag, symbol)					\
    evariable(false, tag, symbol)

#define eop_label		eop_noop

/*
 * Types
 */
enum eopcode {
    /*	<eop_noop> */
    eop_noop,

    /*	<eop_nil> */
    eop_nil,

    /*	<eop_int><immediate>
     *	immediate int32 */
    eop_int,

    /*	<eop_long><immediate>
     *	immediate int64 */
    eop_long,

    /*	<eop_float><immediate>
     *	immediate float64 */
    eop_float,

    /*	<eop_ll><offset>
     *	load constant */
    eop_ll,

    /*	<eop_ld><offset>
     *	load global */
    eop_ld,

    /*	<eop_ldt><offset><type>
     *	load typed global */
    eop_ldt,

    /*	<eop_lb><offset>
     *	load local */
    eop_lb,

    /*	<eop_lbt><offset><type>
     *	load typed local */
    eop_lbt,

    /*	<eop_ls><offset>
     *	offset is always negative
     *	load temporary */
    eop_ls,

    /*	<eop_lh>
     *	load hash value
     *	sp[-1] is the hashtable
     *	r0 is the hash name */
    eop_lh,

    /*	<eop_lhref><argument>
     *	same logic as eop_lvref
     *	iff the entry doesn't exist, it creates one suitable
     *	this is the only condition where a null value is actually
     *	stored in a hashtable, and, it is assumed the eop_shref
     *	following will not store null (but zero in that case, as
     *	it is mean't for math operations), either way, an entry
     *	with a null value just wastes some memory
     */
    eop_lhref,

    /*	<eop_lv><rank>
     *	load vector element
     *	sp[-rank] is the vector
     *	sp[-(rank-1...1)] are the topmost offsets, if any
     *	r0 is the inner offset */
    eop_lv,

    /*	<eop_lvi><offset>
     *	load single dimension vector element */
    eop_lvi,

    /*	<eop_lvref><rank><argument>
     *	o always parse arguments identically to eop_lv, and create a
     *	  t_vectorref special object, that lives only in stack, and
     *	  is like a version of epointer that doesn't allocate memory
     *	o always pushes the created t_vectorref object
     *	values for argument:
     *	0: pushes only the vectorref object
     *	   useful for inplace unary operations
     *	1: pushes, by value, the element value before pushing the vectorref
     *		*sp*
     *		[sp-1]	<vectorref>
     *		[sp-2]	*value*
     *	   useful post increment and decrement
     *	2: pushes, by value, the element value after pushing the vectorref
     *		*sp*
     *		[sp-1]	*value*
     *		[sp-2]	<vectorref>
     *	   useful for inplace binary operations
     *	this opcode is mean't to be used in conjunction with the eop_svref
     *	opcode, and is mean't to implement lexical interpretation of the
     *	source code, and without multiple evaluation of offsets, that may
     *	contain side effects (this could be emulated with other opcodes,
     *	but with complex logic and with a preparation stage, then evaluation
     *	of offsets, save them in temporaries that could just be the stack
     *	by using eop_push/eop_ss*,eop_ls, etc)
     */
    eop_lvref,

    /*	<eop_lr><record><offset><type>
     *	load record field */
    eop_lr,

    /*	<eop_lrref><record><offset><type><argument>
     *	same logic as eop_lvref
     */
    eop_lrref,

    /*	<eop_lc><offset><type>
     *	load class field
     *	bp[-2] is the 'this' pointer */
    eop_lc,

    /*	<eop_lref>
     * dereference pointer */
    eop_lref,

    /*	<eop_pd><offset>
     *	pointer to global */
    eop_pd,

    /*	<eop_pdt><offset><type>
     *	pointer to typed global */
    eop_pdt,

    /*	<eop_pb><offset>
     *	pointer to local */
    eop_pb,

    /*	<eop_pbt><offset><type>
     *	pointer to typed local */
    eop_pbt,

    /*	<eop_pv><rank>
     *	pointer to vector element
     *	r0 is the inner offset
     *	sp[-1...] are the topmost offsets
     *	sp[-rank] is the vector */
    eop_pv,

    /*	<eop_pvi><offset>
     *	pointer to single dimension vector element */
    eop_pvi,

    /*	<eop_pr><record><offset><type>
     *	pointer to record field */
    eop_pr,

    /*	<eop_pp>
     *	pointer to anonymous */
    eop_pp,

    /*	<eop_anon><type><length>
     *	pointer to anonymous object */
    eop_anon,

    /*	<eop_sd><offset>
     *	store global */
    eop_sd,

    /*	<eop_sdt><offset><type>
     *	store typed global */
    eop_sdt,

    /*	<eop_sb><offset>
     *	store local */
    eop_sb,

    /*	<eop_sbt><offset><type:
     *	store typed local */
    eop_sbt,

    /*	<eop_ss><offset>
     *	offset is always negative
     *	store argument or temporary */
    eop_ss,

    /*	<eop_sst><offset><type>
     *	store typed argument or temporary */
    eop_sst,

    /*	<eop_ssv><offset>
     *	offset is always negative
     *	store argument or temporary, creating new mp object if required */
    eop_ssv,

    /*	<eop_sh>
     *	store or delete hash value
     *	sp[-2] is the hashtable
     *	sp[-1] is the hash name
     *	r0 is the hash value, or null for deletion */
    eop_sh,

    /*	<eop_shref><argument>
     *	same logic as eop_svref */
    eop_shref,

    /*	<eop_sv><rank>
     *	store vector element
     *	sp[-rank] is the vector
     *	sp[-(1...rank-1)] are the offsets
     *	r0 is the value */
    eop_sv,

    /*	<eop_svi><offset>
     *	store single dimension vector element
     *	sp[-1] is the vector
     *	r0 is the value */
    eop_svi,

    /*	<eop_svref><argument>
     *	counterpart of eop_lvref, that expects a t_vectorref argument
     *	values for argument:
     *	0: store the current value in the vectorref that is in sp[-1]
     *	   and pops the stack
     *	1: store the current value in the vectorref that is in sp[-1]
     *	   and loads the value in sp[-2], poping the stack twice */
    eop_svref,

    /*	<eop_sr><record><offset><type>
     *	store record field
     *	sp[-1] is the record
     *	r0 is the value */
    eop_sr,

    /*	<eop_srref><argument>
     *	same logic as eop_svref */
    eop_srref,

    /*	<eop_sc><offset><type>
     *	store class field
     *	bp[-2] is the this pointer
     *	r0 is the value */
    eop_sc,

    /*	<eop_sref>
     *	store pointer
     *	sp[-1] is the pointer
     *	r0 is the value */
    eop_sref,

    /*	<eop_jt><offset>
     *	relative true jump */
    eop_jt,

    /*	<eop_jf><offset>
     *	relative false jump */
    eop_jf,

    /*	<eop_j><offset>
     *	relative jump */
    eop_j,

    /*	<eop_jh><offset>
     *	hash jump table */
    eop_jh,

    /*	<eop_jv><offset>
     *	vector jump table */
    eop_jv,

    /*	<eop_enter><local><extra><acount><lcount>[acount+lcount]
     *	local is number of local variables
     *	extra is number of temporary stack slots used
     *	acount is number of typed arguments to check and possibly coerce
     *	lcount is number of typed locals to initialize; <lcount> only matches
     *		<local> if all local variables are typed, using packed type */
    eop_enter,

    /*	<eop_begin><length>
     *	setup for left to right function arguments evaluation */
    eop_begin,

    /*	<eop_off><rank>
     *	convert value to an int32, check vector rank in esp[-rank] and push */
    eop_off,

    /*	<eop_push>
     *	push current value to vm stack */
    eop_push,

    /*	<eop_pusht><type>
     *	push current value to vm stack coercing if required */
    eop_pusht,

    /*	<eop_pushv>
     *	push current value to vm stack making a copy if a mp object */
    eop_pushv,

    eop_pop,

    /*	<eop_call><offset>
     *	call function in ecs[offset] */
    eop_call,

    /*	<eop_meth><offset><type>
     *	call method */
    eop_meth,

    /*	<eop_ecm><offset><type>
     *	excplicit method call, overriding virtual method resolution */
    eop_ecm,

    /*	<eop_blt><offset>
     *	call builtin in ebs[offset] */
    eop_blt,

    /*	<eop_ret>
     *	no arguments function return */
    eop_ret,

    /*	<eop_rett><type>
     *	typed no arguments function return */
    eop_rett,

    /*	<eop_reti><length>
     *	multi argument function return */
    eop_reti,

    /*	<eop_reti><length><type>
     *	typed multi argument function return */
    eop_retit,

    eop_ne,
    eop_lt,
    eop_le,
    eop_eq,
    eop_ge,
    eop_gt,
    eop_and,
    eop_or,
    eop_xor,
    eop_mul2,
    eop_div2,
    eop_shl,
    eop_shr,
    eop_add,
    eop_sub,
    eop_mul,
    eop_div,
    eop_trunc2,
    eop_rem,
    eop_atan2,
    eop_pow,
    eop_hypot,
    eop_complex,

    eop_inc,
    eop_dec,
    eop_bool,
    eop_not,
    eop_com,
    eop_inv,
    eop_neg,
    eop_signbit,
    eop_signum,
    eop_rational,
    eop_integer_p,
    eop_rational_p,
    eop_float_p,
    eop_real_p,
    eop_complex_p,
    eop_number_p,
    eop_finite_p,
    eop_inf_p,
    eop_nan_p,
    eop_num,
    eop_den,
    eop_real,
    eop_imag,
    eop_arg,
    eop_conj,
    eop_floor,
    eop_trunc,
    eop_round,
    eop_ceil,
    eop_abs,
    eop_sqrt,
    eop_cbrt,
    eop_sin,
    eop_cos,
    eop_tan,
    eop_asin,
    eop_acos,
    eop_atan,
    eop_sinh,
    eop_cosh,
    eop_tanh,
    eop_asinh,
    eop_acosh,
    eop_atanh,
    eop_proj,
    eop_exp,
    eop_log,
    eop_log2,
    eop_log10,

    /*	<eop_new><type><length>
     *	new record */
    eop_new,

    /*	<eop_mv><length>
     *	pointer copy length bytes from r0 to sp[-1] and pop stack */
    eop_mv,

    /*	<eop_vnew><type><rank>
     *	new vector
     *	sp[-1...] are the topmost offsets, if any
     *	r0 is the inner vector offset */
    eop_vnew,

    /*	<eop_vnewi><type><length><rank><dimension>[rank]
     *	new vector */
    eop_vnewi,

    /*	<eop_vmv><length>
     *	vector copy length bytes from r0 to sp[-1] and pop stack */
    eop_vmv,

    /*	<eop_renew>
     *	o sp[-1] is the vector
     *	o r0 is the new length
     */
    eop_renew,

    /*	<eop_renewi><length>
     *	o r0 is the vector
     */
    eop_renewi,

    /*	<eop_fref><offset><length><flags>
     *	function pointer */
    eop_fref,

    /*	<eop_apply><length>
     *	o args is number of arguments pushed in stack */
    eop_apply,

    /*	<eop_aret>
     *	signals the return of eop_apply */
    eop_aret,

    /*	<eop_szof>
     *	sets r0 to number of elements of vector in r0 or 0 */
    eop_szof,

    /*	<eop_szof>
     *	sets r0 to vcount (number of extra arguments) */
    eop_szofva,

    /*	<eop_szofdim>
     *	sets r0 to length of dimension specified in r0, of
     *	vector in esp[-1] */
    eop_szofdim,

    /*	<eop_szofdimi><offset>
     *	sets r0 to length of <offset> dimension of vector
     *	in r0, or zero if invalid arguments */
    eop_szofdimi,

    /*	<eop_typeof>
     *	sets r0 to type value of r0 */
    eop_tpof,

    /*	<eop_subtpof>
     *	sets r0 to true if r0 is of type esp[-1] or a derived type
     *	sets r0 to false if not applicable or not a derived type
     *	pops the stack after evaluation */
    eop_subtpof,

    /*	<eop_rankof>
     *	sets r0 to rank of vector in r0, or 0 if it is not a vector
     */
    eop_rankof,

    /*	<eop_vas><length>
     *	o legth is the number of extra (vararg) arguments, that can be zero
     *	after execution:
     *		previous va_count value is saved in thr->egs
     *		va_count is set to length */
    eop_vas,

    /*	<eop_val><length>
     *	o length is the number of extra (vararg) arguments, that can be zero
     *	after execution:
     *		esp is reduced by length
     *		va_count is restored from thr->egs */
    eop_val,

    /*	<eop_lva><length>
     *	o length is the 32 bits bp offset of the valist
     *	r0 is the offset */
    eop_lva,

    /*	<eop_lvai><length><offset>
     *	o length is the bp offset of the valist
     *	o offset is the offset on the varargs */
    eop_lvai,

    /*	<eop_sva><length>
     *	o length is the 32 bits bp offset of the valist
     *	sp[-1] is the offset
     *	r0 is the value */
    eop_sva,

    /*	<eop_svai><length><offset>
     *	o length is the bp offset of the valist
     *	r0 is the value */
    eop_svai,

    /* <eop_pva><length>
     *	create reference to local vararg variable
     *	o length is the bp offset of the valist
     *	r0 is the dynamic offset from the vararg offset */
    eop_pva,

    /*	<eop_try>
     *	  eop_try jumps over a jump opcode, so that it can execute
     *	  the non exception code, example:
     *		eop_try
     *		jmp L.catch	<- throw target (skipped by eop_try)
     *		...		<- normal code
     *		L.catch:
     *		...		<- catch type check code
     * after execution, 4 stack slots are used to save previous try stack
     * offset (1), save per thread gc protected stack (2), save ebp (3)
     * and save throw target ip (4) */
    eop_try,

    /*	<eop_catch>
     *	executes vm code to check a throwed exception
     * after execution, the 4 try stack slots are restored */
    eop_catch,

    /*	<eop_throw><argument>
     *	if argument == 0 throw r0 contents to the topmost try
     *  else throw last exception object to the  topmost try
     * argument is always a boolean byte */
    eop_throw,

    /*	<eop_unwind>
     *	basically does the same cleanup as eop_catch, but only
     *	executed after the try block has been executed, that is,
     *	no exception was thrown, thus, use a cheaper/faster cleanup */
    eop_unwind,

    /*	<eop_exit>
     *	returns from the main vm function */
    eop_exit,

    /*	<eop_setup><index><type>
     *	initialize once a statically typed global variable to zero */
    eop_setup,

    /***********************************************************************
     * optimization predicate with optional assignment, or error opcodes
     ***********************************************************************/
    eop_intp_0,		/* set to zero if an integer */
    eop_intp_v,		/* noop if an integer */
    eop_intp_n1,	/* set to minus one if an integer */
    eop_realp_v,	/* noop if a real number */
    eop_nump_0,		/* set to zero if a number */
    eop_nump_v,		/* noop if a number */

    /***********************************************************************
     * annotation values
     ***********************************************************************/
    eop_note,		/* file name and line number */
    eop_block,		/* boundary of code after conditional jump */
};

#define code_flags_implicit			0x40000000
struct ecode_label {
    ecode_t		 code;
    eint32_t		 flags;
    eint32_t		 offset;
    elabel_t		*label;
};

struct ecode_simple {
    ecode_t		 code;
    eint32_t		 flags;
};

struct ecode_immediate {
    ecode_t		 code;
    eint32_t		 flags;
    edata_t		 data;
};

struct ecode_stack {
    ecode_t		 code;
#define code_stack_incdec			1
#define code_stack_incdec_complex		2
#define code_stack_binarg			4
    eint32_t		 flags;
    eint32_t		 offset;
    eint32_t		 length;
    eint32_t		 type;
    etag_t		*tag;
};

struct ecode_symbol {
    ecode_t		 code;
#define code_store_incdec			1
    eint32_t		 flags;
    eint32_t		 ssa;
    eint32_t		 bit;
    esymbol_t		*symbol;
};

struct ecode_hash {
    ecode_t		 code;
    eint32_t		 flags;
    eint32_t		 arg;
};

struct ecode_vector {
    ecode_t		 code;
    eint32_t		 flags;
    eint32_t		 offset;
    eint32_t		 length;
    eint32_t		 type;
    eint32_t		 rank;
    eint32_t		 arg;
    eint32_t		 dims[1];
};

struct ecode_record {
    ecode_t		 code;
    eint32_t		 flags;
    erecord_t		*record;
    esymbol_t		*symbol;
    eint32_t		 arg;
};

struct ecode_enter {
    ecode_t		 code;
    eint32_t		 flags;
    eint32_t		 local;
    eint32_t		 extra;
    eint32_t		 acount;
    eint32_t		 lcount;
    eint32_t		 types[1];
};

struct ecode_apply {
    ecode_t		 code;
    eint32_t		 flags;
    eint32_t		 offset;
    eint32_t		 length;
    eint32_t		 type;
    eint32_t		 arg;
    esymbol_t		*symbol;
};

struct ecode_return {
    ecode_t		 code;
#define code_return_implicit			1
    eint32_t		 flags;
    eint32_t		 length;
    eint32_t		 type;
    etag_t		*tag;
};

struct ecode_logic {
    ecode_t		 code;
#define code_logic_except			1
    eint32_t		 flags;
};

struct ecode_except {
    ecode_t		 code;
    eint32_t		 flags;
    eint32_t		 arg;
};

struct ecode_note {
    ecode_t		 code;
    eint32_t		 flags;
    evector_t		*name;
    eint32_t		 line;
    eobject_t		 note;
};

union east_node {
    ecode_t		code;
    ecode_label_t	clabel;
    ecode_simple_t	csimple;
    ecode_immediate_t	cimmediate;
    ecode_stack_t	cstack;
    ecode_symbol_t	csymbol;
    ecode_hash_t	chash;
    ecode_vector_t	cvector;
    ecode_record_t	crecord;
    ecode_enter_t	center;
    ecode_apply_t	capply;
    ecode_return_t	creturn;
    ecode_logic_t	clogic;
    ecode_except_t	cexcept;
    ecode_note_t	cnote;
};

struct east_list {
    east_list_t		*next;
    east_node_t		*node;
    eint32_t		 itype;
    eint32_t		 rtype;
    eint32_t		 ltype;
};

#define east_next(ast)			(ast)->next
#define east_node(ast)			(ast)->node

#define east_tail()			fn_current->tail

/* FIXME there should be a call to create an east_list_t and another to
 * "link" it, but that may case some confusing code, in the sense of
 * too much calls, like east_list_append(east_link(ecode_foo_bar(...))) */
extern east_list_t *
east_no_link(east_node_t *node);
extern east_list_t *
east_label_no_link(east_node_t *node);

extern east_list_t *
east_link(east_node_t *node);

extern east_list_t *
east_label_link(east_node_t *node);

#define east_logic_link(node)		east_link(node)

struct elabel {
    elabel_t		*next;
    eobject_t		 value;
    eint32_t		 ident;
    eint32_t		 stack;	/* number of stack operands at entry point */
    east_list_t		*list;
    evector_t		*jumps;	/* branches targeting label */

    /* basic blocks that reach this basic block */
    evector_t		*srcs;

    /* basic blocks that this basic block reach */
    evector_t		*dsts;

    /* dfa basic block entry information */
    evector_t		*dfa;

    /* type inference information */
    evector_t		*infer_stack;
    evector_t		*infer_types;

    /* bit vector of values set */
    empz_t		 set;

    /* bit vector of values used (before assigned, if assigned) */
    empz_t		 use;

    /* bit vector of values that may come from multiple sources */
    empz_t		 phi;

    /* loop nesting level of basic block */
    eint32_t		 loop;

    /* set if a value is stored using a pointer */
    eint32_t		 sref	: 1;

    /* set if previous label/block ends in an unconditional jump */
    eint32_t		 ijmp	: 1;

    /* set if label/block ends in an unconditional jump */
    eint32_t		 ojmp	: 1;

    /* set if a block, only reachable if not following a conditional branch */
    eint32_t		 block	: 1;
};

#define elabel_list(label)		(label)->list

struct eblock {
    eblock_t	*parent;

    /*	Used only by switch. Hash entries format is:
     *	name:	o an eentry_t from fn_current->table,
     *		  that is, a pointer to a function scope label entry
     *		o there is one entry with a null key, that signs the
     *		  default jump address (or end of switch block)
     *		  this entry is removed when generating the final
     *		  evswitch_t or ehswitch_t object.
     *	value:	a cons of (integer . null)
     *		o where 'integer' is the value that will be tested at
     *		  runtime.
     *		o for the special entry with null name, the 'integer'
     *		  entry is actually a an eentry_t from fn_current->table
     *		  exactly like the name field.
     *		when building the label table, the ending null is
     *		replaced with the jump distance, and finally, when all
     *		jumps are resolved, the hash table is discarded, and
     *		an evswitch_t or ehswitch_t used in it's place.
     */
    ehash_t	*table;

    /* vectors of labels to as ordered as possible jumps/labels and
     * reduce jump threading optimization, that may consume quite
     * some time in large programs */
    evector_t	*t_jump;
    evector_t	*f_jump;

    /* for and while loops are constructed expanding first test so
     * that it can usually be copy/constant propagated and frequently
     * removed */
    elabel_t	*label;		/* continue and extra special usage label */
    etoken_t	 token;		/* block type */

    /* do not release/reuse eblock_t objets with flags set
     * until gotos are validated */
#define block_has_label		1
#define block_has_goto		2
    eint32_t	 flags;
};

/*
 * Prototypes
 */
PUBLIC extern void
ecode(void);

extern east_node_t *
ecode_label(elabel_t *label);

extern east_node_t *
ecode_block(elabel_t *label);

extern ebool_t
ecode_false_p(east_node_t *code);

extern east_node_t *
ecode_branch(ecode_t code, elabel_t *label);

east_node_t *
ecode_simple(ecode_t code);

extern east_node_t *
ecode_symbol_load(esymbol_t *symbol);

extern east_node_t *
ecode_symbol_store(esymbol_t *symbol);

extern east_node_t *
ecode_symbol_pointer(esymbol_t *symbol);

extern east_node_t *
ecode_symbol_setup(esymbol_t *symbol, eint32_t type);

#define ecode_class_load(symbol)	ecode_class(eop_lc, symbol)

#define ecode_class_store(symbol)	ecode_class(eop_sc, symbol)

extern east_node_t *
ecode_class(ecode_t code, esymbol_t *symbol);

extern east_node_t *
ecode_hash(ecode_t code);

extern east_node_t *
ecode_hash_ref(ecode_t code, eint32_t arg);

extern east_node_t *
ecode_vector_new(eint32_t type, eint32_t rank);

extern east_node_t *
ecode_vector_renew(void);

extern east_node_t *
ecode_vector_renew_static(eint32_t length);

extern east_node_t *
ecode_vector_new_static(eint32_t type, eint32_t length, eint32_t rank,
			eobject_t *values);

extern east_node_t *
ecode_vector_copy(eint32_t length);

extern east_node_t *
ecode_vector_load(eint32_t rank);

extern east_node_t *
ecode_vector_store(eint32_t rank);

extern east_node_t *
ecode_vector_pointer(eint32_t rank);

extern east_node_t *
ecode_vector_offset(eint32_t rank);

extern east_node_t *
ecode_vector_load_static(eint32_t offset);

extern east_node_t *
ecode_vector_store_static(eint32_t offset);

extern east_node_t *
ecode_vector_pointer_static(eint32_t offset);

extern east_node_t *
ecode_vector_load_ref(eint32_t rank, eint32_t arg);

extern east_node_t *
ecode_vector_store_ref(eint32_t arg);

extern east_node_t *
ecode_vector_sizeof(void);

extern east_node_t *
ecode_vector_sizeofdim(void);

extern east_node_t *
ecode_vector_sizeofdim_static(eint32_t arg);

extern east_node_t *
ecode_record_new(erecord_t *record);

extern east_node_t *
ecode_record_new_anon(erecord_t *record);

extern east_node_t *
ecode_record_copy(erecord_t *record);

#define ecode_record_load(record, symbol)				\
    ecode_record(eop_lr, record, symbol)

#define ecode_record_store(record, symbol)				\
    ecode_record(eop_sr, record, symbol)

#define ecode_record_pointer(record, symbol)				\
    ecode_record(eop_pr, record, symbol)

extern east_node_t *
ecode_record(ecode_t code, erecord_t *record, esymbol_t *symbol);

extern east_node_t *
ecode_record_load_ref(erecord_t *record, esymbol_t *symbol, eint32_t arg);

extern east_node_t *
ecode_record_store_ref(eint32_t arg);

extern east_node_t *
ecode_record_typeof(void);

extern east_node_t *
ecode_record_subtypeof(void);

extern east_node_t *
ecode_enter_init(void);

extern east_node_t *
ecode_enter_finish(efunction_t *function);

extern east_node_t *
ecode_stack_push(ecode_t code, etag_t *tag, eint32_t hint);

#define ecode_push()							\
    ecode_stack_push(eop_push, null, 0)

#define ecode_push_value()						\
    ecode_stack_push(eop_pushv, null, 0)

#define ecode_push_tag(tag)						\
    ecode_stack_push(eop_push, tag, 0)

#define ecode_push_value_tag(tag)					\
    ecode_stack_push(eop_pushv, tag, 0)

#define ecode_push_incdec()						\
    ecode_stack_push(eop_pushv, null, code_stack_incdec)

#define ecode_push_incdec_complex()					\
    ecode_stack_push(eop_push, null, code_stack_incdec_complex)

#define ecode_push_binarg()						\
    ecode_stack_push(eop_push, null, code_stack_binarg)

extern east_node_t *
ecode_stack_load(eint32_t offset);

extern east_node_t *
ecode_stack_store_value(eint32_t offset);

extern east_node_t *
ecode_stack_store_tag(eint32_t offset, etag_t *tag);

extern east_node_t *
ecode_stack_store_value_tag(eint32_t offset, etag_t *tag);

extern east_node_t *
ecode_stack_pop(void);

extern east_node_t *
ecode_call(esymbol_t *symbol);

extern east_node_t *
ecode_builtin(esymbol_t *symbol, eint32_t length);

extern east_node_t *
ecode_apply(eint32_t length);

extern east_node_t *
ecode_function(esymbol_t *symbol, eint32_t length, eint32_t arg);

extern east_node_t *
ecode_method(ecode_t code, esymbol_t *symbol, eint32_t type);

extern east_node_t *
ecode_return(eint32_t length, etag_t *tag);

extern east_node_t *
ecode_vararg(ecode_t code, eint32_t length);

extern east_node_t *
ecode_vararg_static(ecode_t code, eint32_t length, eint32_t offset);

extern east_node_t *
ecode_logic(ecode_t code);

#define ecode_unary(code)		ecode_logic(code)

#define ecode_binary(code)		ecode_logic(code)

extern east_node_t *
ecode_except(ecode_t code);

extern east_node_t *
ecode_except_arg(ecode_t code, eint32_t arg);

extern east_node_t *
ecode_note(evector_t *name, eint32_t line);

extern void
elabel_branch_remove(elabel_t *label, east_node_t *node);

extern void
elabel_branch_insert(elabel_t *label, east_node_t *node);

extern void
elabel_branch_switch(elabel_t *prev, elabel_t *next, east_node_t *node);

extern elabel_t *
ecreate_label(efunction_t *function);

extern void
eremove_label(efunction_t *function, elabel_t *label);

#define eremove_block(function, label)	eremove_label(function, label)

extern void
edefine_builtin(esymbol_t *name, void (*builtin)(eint32_t),
		eint32_t args, ebool_t varargs);

extern eentry_t *
eannotation(eobject_t object);

extern etag_t *
eeval(eobject_t object);

extern etag_t *
elanguage(etoken_t token, eobject_t list);

/* add object to constant table, add opcode to load it in function
 * being processed, and return the associated object tag */
extern etag_t *
eliteral(eobject_t object);

extern void
evariable(ebool_t global, etag_t *tag, esymbol_t *symbol);

extern eint32_t
econstant(eobject_t object);

extern void
init_code(void);

extern void
finish_code(void);

/*
 * Externs
 */
extern efunction_t *fn_root;
extern efunction_t *fn_current;

#endif /* _ecode_h */
